package com.ece.demo.init;

import java.awt.Adjustable;
import java.awt.Font;
import java.awt.FontFormatException;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.imageio.ImageIO;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;

import com.bulletphysics.dynamics.character.KinematicCharacterController;
import com.ece.demo.controller.commands.charactor.SpriteMoveBackCommand;
import com.ece.demo.controller.commands.charactor.SpriteMoveForwardCommand;
import com.ece.demo.controller.commands.charactor.SpriteMoveLeftCommand;
import com.ece.demo.controller.commands.charactor.SpriteMoveRightCommand;
import com.ece.demo.normal.NormalMapShader;
import com.ece.demo.normal.SquareSprite;

import dev.ece.core.cells.model.action.ActionManager;
import dev.ece.core.cells.model.action.ActionManager.Action;
import dev.ece.core.cells.model.base.FboUI;
import dev.ece.core.cells.model.base.SquareModel;
import dev.ece.core.cells.world.World2D;
import dev.ece.core.cells.world.World3D;
import dev.ece.core.controller.Command;
import dev.ece.core.controller.Controller;
import dev.ece.core.controller.OtherKeyCommand;
import dev.ece.core.controller.commands.CameraBackward;
import dev.ece.core.controller.commands.CameraForward;
import dev.ece.core.controller.commands.CameraTurnBottom;
import dev.ece.core.controller.commands.CameraTurnLeft;
import dev.ece.core.controller.commands.CameraTurnRight;
import dev.ece.core.controller.commands.CameraTurnTop;
import dev.ece.core.engine.LoaderFlush;
import dev.ece.core.engine.LoaderQueue;
import dev.ece.core.fog.Fog;
import dev.ece.core.light.Light;
import dev.ece.core.light.Lights;
import dev.ece.core.light.Material;
import dev.ece.core.shader.DepthShader;
import dev.ece.core.texture.FBO;
import dev.ece.core.texture.Texture;
import dev.ece.suit.bloom.BloomHDRComponent;
import dev.ece.suit.bloom.BloomHDRShader;
import dev.ece.suit.heightmap.HeightMapModel;
import dev.ece.suit.heightmap.HeightMapSprite;
import dev.ece.suit.loader.AbstractModel3D;
import dev.ece.suit.loader.Sprite3D;
import dev.ece.suit.multisample.MultiSampleComponent;
import dev.ece.suit.multisample.MultiSampleShader;
import dev.ece.suit.text.TextLibrary;
import dev.ece.suit.ui.AnimateImage;
import dev.ece.suit.ui.Button;
import dev.ece.suit.ui.Component;
import dev.ece.suit.ui.Fonts;
import dev.ece.suit.ui.Image;
import dev.ece.suit.ui.Label;
import dev.ece.suit.ui.ScrollBar;
import dev.ece.suit.ui.ScrollPane;
import dev.ece.suit.ui.SelectableGroup;
import dev.ece.suit.ui.TextArea;
import dev.ece.suit.ui.VariableWidthComponent;
import dev.ece.suit.ui.event.ButtonListener;
import dev.ece.suit.ui.event.ClickListener;
import dev.ece.suit.ui.event.ScrollBarListener;
import dev.ece.suit.ui.event.ScrollPaneContentItemsListener;
import dev.ece.suit.ui.event.SelectableGroupListener;
import dev.ece.suit.ui.utils.Alignment;
import dev.ece.suit.utils.ShaderFunctions;
import dev.ece.util.lib.IntOffset;
import dev.ece.util.lib.IntRectangle;
import dev.ece.util.lib.Rectangle;
import dev.ece.util.lib.Vector2i;
import dev.ece.util.lib.Vector4f;
import dev.ece.util.tools.Constants;
import dev.ece.util.tools.Projection;
import dev.ece.util.tools.Screen;
import dev.ece.util.tools.ploy.TextureDefaultPloy;
import dev.ece.util.tools.ploy.TextureMipmapPloy;
import dev.ece.util.tools.transformations.Translation;

/**
 * @author 潜行的青衣
 * @see
 */
public class DemoInit {
	
	public static boolean RUNNING = true;
	
	public static TextLibrary initTextLibrary() throws FileNotFoundException, FontFormatException, IOException {
		//所有常用字集合
		StringBuffer s = new StringBuffer();
		s.append("　 ");
		for(char c='!'; c<='~'; c++) {
			s.append(c);
		}
		for(char c = '！'; c<='／'; c++) {
			s.append(c);
		}
		BufferedReader reader = null;
		try {
			//读入常用汉字
			reader = new BufferedReader(new InputStreamReader(Constants.getGameFileInputStream("/assets/fonts/chars.txt")));
			String str = null;
			while((str = reader.readLine()) != null) {
				s.append(str);
			}
		} finally {
			IOUtils.closeQuietly(reader);
		}
		//初始化字库 如果太多字会出问题，不要多于5000字
		TextLibrary textLibrary = new TextLibrary(new Font("新宋体", Font.PLAIN, 16), s.toString());
		textLibrary.initialize();
		Fonts.setSystemTextLibrary(textLibrary);
		return textLibrary;
	}
	
	public static MultiSampleComponent initMultiSample(Screen screen) throws Exception {
		//几倍抗锯齿 2、4、8、16
		int maxsample = GL11.glGetInteger(GL30.GL_MAX_SAMPLES);
		int samples = Math.min(maxsample, 16);
		FBO fbo = FBO.newInstance(samples, screen.getWidth(), screen.getHeight());
		MultiSampleShader shader = MultiSampleShader.createMultiSampleShader();
		FboUI model = new FboUI(screen.getWidth(), screen.getHeight());
		MultiSampleComponent component = new MultiSampleComponent(model, shader, fbo);
		return component;
	}
	
	public static BloomHDRComponent initHDR(World2D world, Screen screen) throws Exception {
		BloomHDRShader shader = BloomHDRShader.createBloomHDRShader();
		BloomHDRComponent component = new BloomHDRComponent(shader, screen.getWidth(), screen.getHeight());
		component.setGamma(2.2f);
		component.setExposure(1.0f);
		world.add(component);
		return component;
	}
	
	public static Sprite3D<Float> initSprite(World3D world, Map<String, String> functions) throws Exception {
		try {
			Sprite3D<Float> sprite = Sprite3DFactoryDemo.getSprite3DFactory().loadBoneSprite3D("ninja.ms3d");
			AbstractModel3D<Float> model = sprite.getModel();
			ActionManager.addAction(model.getActions(), new Action<Float>("stand", 0f, 0f, true));
			ActionManager.addAction(model.getActions(), new Action<Float>("move-1", 0f, model.createCurrent(14), true));
			ActionManager.addAction(model.getActions(), new Action<Float>("stand-1", model.createCurrent(183), 12f, true));
			ActionManager.addAction(model.getActions(), new Action<Float>("attack-1", model.createCurrent(74), model.createCurrent(83), false));
			ActionManager.addAction(model.getActions(), new Action<Float>("jump-1", model.createCurrent(94), model.createCurrent(102), false));
			ActionManager.addAction(model.getActions(), new Action<Float>("attack-2", model.createCurrent(126), model.createCurrent(133), false));
			sprite.action("stand-1");
			return sprite;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static World3D initWorld(Screen screen) {
		try {
			Class.forName("com.ece.demo.init.Model3DTextureLoader");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		Projection shadowProjection = new Projection();
		shadowProjection.set(-80, 80, -80, 80, 1, 1000);
		//shadowProjection.set(-1, 1, -1, 1, 1.0f, 1000);
		int size = 4096;
		World3D world = new World3D(screen, size, size, shadowProjection, 0);
		return world; 
	}
	
	public static Controller initController(World2D desktop, World3D world, BloomHDRComponent bloomhdr, HeightMapModel heightModel, Sprite3D<Float> sprite, KinematicCharacterController ch_controller) {
		float speedAngle = 60f;
		float speed = 3f;
		Controller controller = new Controller();
		controller.put(GLFW.GLFW_KEY_RIGHT, new CameraTurnRight(world.getCamera(), speedAngle));
		controller.put(GLFW.GLFW_KEY_LEFT, new CameraTurnLeft(world.getCamera(), speedAngle));
		controller.put(GLFW.GLFW_KEY_PAGE_UP, new CameraTurnTop(world.getCamera(), speedAngle));
		controller.put(GLFW.GLFW_KEY_PAGE_DOWN, new CameraTurnBottom(world.getCamera(), speedAngle));
		controller.put(GLFW.GLFW_KEY_UP, new CameraForward(world.getCamera(), speed));
		controller.put(GLFW.GLFW_KEY_DOWN, new CameraBackward(world.getCamera(), speed));
		controller.put(GLFW.GLFW_KEY_A, new SpriteMoveLeftCommand(sprite, ch_controller, world.getCamera(), speed));
		controller.put(GLFW.GLFW_KEY_D, new SpriteMoveRightCommand(sprite, ch_controller, world.getCamera(), speed));
		controller.put(GLFW.GLFW_KEY_W, new SpriteMoveForwardCommand(sprite, ch_controller, world.getCamera(), speed));
		controller.put(GLFW.GLFW_KEY_S, new SpriteMoveBackCommand(sprite, ch_controller, world.getCamera(), speed));
		controller.setOtherKeyCommand(new OtherKeyCommand() {

			@Override
			public void onKeyPress(int key) {
				System.out.println(String.format("按键%s按下", key));
			}

			@Override
			public void onKeyRelease(int key) {
				System.out.println(String.format("按键%s抬起", key));
			}
			
		});
		controller.put(GLFW.GLFW_KEY_1, new Command() {

			@Override
			public void onExecute() {}

			@Override
			public void onKeyPress() {
				Lights.getLight(0).setEnable(!Lights.getLight(0).isEnable());
			}

			@Override
			public void onKeyRelease() {}
		});
		controller.put(GLFW.GLFW_KEY_2, new Command() {

			@Override
			public void onExecute() {}

			@Override
			public void onKeyPress() {
				Lights.getLight(1).setEnable(!Lights.getLight(1).isEnable());
			}

			@Override
			public void onKeyRelease() {}
		});
		controller.put(GLFW.GLFW_KEY_3, new Command() {

			@Override
			public void onExecute() {}

			@Override
			public void onKeyPress() {
				bloomhdr.setEnable(!bloomhdr.isEnable());
			}

			@Override
			public void onKeyRelease() {}
		});
		controller.put(GLFW.GLFW_KEY_4, new Command() {

			private Component component;
			
			private int status;
			
			@Override
			public void onExecute() {}

			@Override
			public void onKeyPress() {
				if(status == 2 && component != null) {
					component.setVisible(!component.isVisible());
				} else {
					if(status == 0) {
						status = 1;
						LoaderQueue.push((LoaderFlush flush) -> {
							String background = "/assets/images/system/system.png";
							//添加一个九宫格面板
							IntRectangle textureClipping = new IntRectangle(2, 258, 230, 321);
							IntRectangle centerClipping = new IntRectangle(34, 34, 34, 12);
							component = new Component(
									background, textureClipping, desktop.getScreen().getWidth(), desktop.getScreen().getHeight(), centerClipping);
							//添加一个图片
							Image image = new Image("/assets/images/skybox/front.jpg", 200, 200);
							component.add(image);
							//添加一个按钮
							Button button = new Button(background, new IntRectangle(4, 30, 112, 58), 100, 30);
							//按钮按下时纹理坐标的偏移量
							button.setPressedOffset(115, 0);
							button.setFontSize(15);
							button.setText("关闭");
							button.addButtonListener(new ButtonListener() {

								@Override
								public void onPressed(Button button) {
									System.out.println("pressed");
								}

								@Override
								public void onClicked(Button button) {
									component.setVisible(false);
									
								}

								@Override
								public void onReleased(Button button) {
									System.out.println("released");
								}
							});
							component.add(button);
							
							//横向带滚动条的面板
							Button scrollBarButton = new Button(background, new IntRectangle(878, 172, 920, 214), 20, 20);
							ScrollBar scrollBar = new ScrollBar(background, new IntRectangle(54, 118, 112, 194), 
									300 - 6, 20, 
									new IntRectangle(3, 3, 3, 3), 
									Adjustable.HORIZONTAL, scrollBarButton,
									40, 0, 100);
							scrollBar.addScrollBarListener(new ScrollBarListener() {

								@Override
								public void onScroll(ScrollBar scrollBar, int value, int min, int max) {
									System.out.println(String.format("scroll:%s", value));
								}
								
							});
							
							ScrollPane<Image> pane = new ScrollPane<Image>(background, new IntRectangle(54, 118, 112, 194), 300, 300, new IntRectangle(3, 3, 3, 3), scrollBar);
							pane.position.set(- 300, 100);
							pane.setPadding(new IntRectangle(3, 3, 3, 3));
							//需要先添加监听
							pane.addScrollPaneContentItemsListener(new ScrollPaneContentItemsListener<Image>() {

								@Override
								public void onContentItemChanged(ScrollPane<Image> pane, Vector<Image> items) {
									//添加删除contentItem时，这里调整contentItem坐标
									Rectangle viewer = pane.getContentViewRectangle();
									float hw = 0;
									float x = viewer.left;
									for(Image item: items) {
										hw = item.getWidth() / 2;
										x += hw;
										item.position.set(x, viewer.top - item.getHeight() / 2);
										x += hw;
									}
								}
								
							});
							
							Rectangle viewer = pane.getContentViewRectangle();
							float viewerWidth = viewer.right - viewer.left;
							float itemWidth = viewerWidth / 5;
							List<Image> images = new ArrayList<Image>();
							Image item = null;
							for(int i=0; i<10; i++) {
								item = new Image(background, new IntRectangle(3, 164, 41, 202), itemWidth, itemWidth);
								images.add(item);
							}
							//添加contentItem
							pane.addAllContentItem(images);
							component.add(pane);
							//可以通过这个方法改变滚动位置
							//scrollBar.scroll(50);
							
							//纵向带滚动条的面板
							scrollBarButton = new Button(background, new IntRectangle(878, 172, 920, 214), 20, 20);
							scrollBar = new ScrollBar(background, new IntRectangle(54, 118, 112, 194), 
									20, 300, 
									new IntRectangle(3, 3, 3, 3), 
									Adjustable.VERTICAL, scrollBarButton);
							final ScrollPane<Button> vpane = new ScrollPane<Button>(background, new IntRectangle(54, 118, 112, 194), 300, 300, new IntRectangle(3, 3, 3, 3), scrollBar);
							//设置scrollbar不在内部使用
							vpane.setShowVerticalScrollBar(false);
							vpane.position.set(300, 100);
							vpane.setPadding(new IntRectangle(3, 3, 3, 3));
							//需要先添加监听
							vpane.addScrollPaneContentItemsListener(new ScrollPaneContentItemsListener<Button>() {

								@Override
								public void onContentItemChanged(ScrollPane<Button> pane, Vector<Button> items) {
									//添加删除contentItem时，这里调整contentItem坐标
									Rectangle viewer = pane.getContentViewRectangle();
									float hh = 0;
									float y = viewer.top;
									for(Button item: items) {
										hh = item.getHeight() / 2;
										y -= hh;
										item.position.set(viewer.left + item.getWidth() / 2, y);
										y -= hh;
									}
								}
								
							});
							
							viewer = pane.getContentViewRectangle();
							viewerWidth = viewer.right - viewer.left;
							itemWidth = viewerWidth / 5;
							SelectableGroup<Button> buttonGroup = new SelectableGroup<Button>();
							buttonGroup.addSelectableGroupListener(new SelectableGroupListener<Button>() {

								@Override
								public void onSelectionSelected(SelectableGroup<Button> group, Button selection) {
									vpane.scroll(selection);
								}
							});
							List<Button> buttons = new ArrayList<Button>();
							Button buttonitem = null;
							for(int i=0; i<10; i++) {
								buttonitem = new Button(background, new IntRectangle(3, 164, 41, 202), itemWidth, itemWidth);
								buttonitem.setSelectableGroup(buttonGroup);
								buttonitem.setText(String.valueOf(i));
								buttons.add(buttonitem);
							}
							
							//添加contentItem
							vpane.addAllContentItem(buttons);
							
							scrollBar.position.set(465, 100);
							
							component.add(scrollBar);
							component.add(vpane);
							
							Button up = new Button(background, new IntRectangle(2, 6, 18, 22), 30, 30);
							up.position.set(110, 115);
							up.addClickListener(new ClickListener() {

								@Override
								public void onClicked(Component component) {
									buttonGroup.selectPrevious(true);
								}
							});
							Button down = new Button(background, new IntRectangle(23, 6, 39, 22), 30, 30);
							down.position.set(110, 85);
							down.addClickListener(new ClickListener() {

								@Override
								public void onClicked(Component component) {
									buttonGroup.selectNext(true);
								}
								
							});
							component.add(up);
							component.add(down);
							
							button = new Button(background, new IntRectangle(4, 30, 112, 58), 200, 30);
							button.position.set(0, -50);
							//按钮按下时纹理坐标的偏移量
							button.setPressedOffset(115, 0);
							button.setFontSize(15);
							button.setText("删除");
							button.addButtonListener(new ButtonListener() {

								@Override
								public void onPressed(Button button) {}

								@Override
								public void onClicked(Button button) {
									int index = 3;
									if(index < pane.contentItemsSize()) {
										pane.removeContentItem(index);
									}
								}

								@Override
								public void onReleased(Button button) {}
								
							});
							component.add(button);
							
							Component blood = new Component(background, new IntRectangle(84, 362, 282, 390), 300, 30, new IntRectangle(31, 12, 31, 12)) {

								/**
								 * 
								 */
								private static final long serialVersionUID = 1L;

								@Override
								protected void glPushScissor() {
									//不使用剪裁
								}

								@Override
								public void glPopScissor() {
									//不使用剪裁
								}
								
								
							};
							blood.position.setY(50);
							//血条内部IntOffset:左中右切割纹理
							VariableWidthComponent bloodvalue = new VariableWidthComponent(
									background, new IntRectangle(229, 90, 261, 100), blood.getWidth() - 30, 12, new IntOffset(5, 5));
							bloodvalue.setValue(80);
							
							blood.add(bloodvalue);
							
							component.add(blood);
							
							
							Label label = new Label("你你你", 300, 30);
							label.position.setY(50);
							label.setAlignment(Alignment.CENTER);
							
							component.add(label);
							
							String aimgbackground = "/assets/images/system/common.png";
							AnimateImage img = new AnimateImage(aimgbackground, new IntRectangle(1200, 622, 1312, 734), 100, 100, new Vector2i[] {
								new Vector2i(),
								new Vector2i(134, 0),
								new Vector2i(272, 0),
								new Vector2i(404, 0)
							});
							//每10帧播放图片1帧
							img.setFrames(10);
							component.add(img);
							
							//textarea
							//横向带滚动条的面板
							Button hScrollBarButton = new Button(background, new IntRectangle(878, 172, 920, 214), 20, 20);
							ScrollBar hScrollBar = new ScrollBar(background, new IntRectangle(54, 118, 112, 194), 
									270, 20, 
									new IntRectangle(3, 3, 3, 3), 
									Adjustable.HORIZONTAL, hScrollBarButton,
									40, 0, 100);
							//纵向带滚动条的面板
							Button vScrollBarButton = new Button(background, new IntRectangle(878, 172, 920, 214), 20, 20);
							ScrollBar vScrollBar = new ScrollBar(background, new IntRectangle(54, 118, 112, 194), 
									20, 270, 
									new IntRectangle(3, 3, 3, 3), 
									Adjustable.VERTICAL, vScrollBarButton);
							final TextArea text = new TextArea(background, new IntRectangle(54, 118, 112, 194), 300, 300, new IntRectangle(3, 3, 3, 3), hScrollBar, vScrollBar);
							String[] lines = new String[] {
									"FBX模型真是神烦，还是没能搞定，搁置中\n",
									"按键1:灯光0开关\n", 
									"按键2:灯光1开关\n", 
									"按键3:HDR开关\n",
									"按键4:UI展示开关\n",
									"你再看,再看就把你吃掉!ABCDEFGHIJ[color=#ff0000]KLMNO[/]PQRSTUVWXYZab[color=#0000ff]cdefghijk[/]lmnopqrstuvwxyz\n",
									"1234567890\n",
									"为了凑行数1\n",
									"为了凑行数2\n",
									"为了凑行数3\n",
									"为了凑行数4\n",
									"为了凑行数5\n",
									"为了凑行数6"
								};
							text.position.setY(-200);
							text.setFontSize(24);
							text.setPadding(new IntRectangle(3, 3, 3, 3));
							text.setFontColor(new Vector4f(1, 1, 0, 1));
							text.setText(StringUtils.join(lines));
							component.add(text);
							flush.finish();
							desktop.add(component);
							status = 2;
						});
					}
				}
			}

			@Override
			public void onKeyRelease() {}
		});
		return controller;
	}
	
	public static HeightMapSprite initHeightMap(World3D world, Map<String, String> functions) throws IOException {
		Texture texture = Texture.newInstance(
				Constants.getGameFileInputStream("/assets/images/grass.png"), 
				TextureMipmapPloy.getInstance());
		HeightMapModel model = new HeightMapModel(
				ImageIO.read(Constants.getGameFileInputStream("/assets/images/land.png")), 1f, 10f, 1/4f);
		HeightMapSprite sprite = new HeightMapSprite(model, texture);
		world.getCamera().position.set(20, model.getHeight(20, 20) + 2, 20);
		world.getCamera().center.set(world.getCamera().position);
		world.getCamera().center.setZ(world.getCamera().center.getZ() - 1);
		return sprite;
	}
	
	public static SquareSprite initNormalMapping(World3D world, Map<String, String> functions) throws FileNotFoundException {
		Texture texture = Texture.newInstance(
				Constants.getGameFileInputStream("/assets/images/normal/Vol_42_2_Base_Color.png"),
				TextureDefaultPloy.getInstance());
		
		Texture normal = Texture.newInstance(
				Constants.getGameFileInputStream("/assets/images/normal/Vol_42_2_Normal.png"),
				TextureDefaultPloy.getInstance());
		
		NormalMapShader shader = new NormalMapShader(
				Constants.getGameFileInputStream("/assets/shaders/normalmapping.vert"), 
				Constants.getGameFileInputStream("/assets/shaders/normalmapping.frag"),
				functions);
		
		SquareModel model = new SquareModel(1, 1);
		//设置材质
		Material material = model.getMaterial();
		material.getDiffuse().set(0.8f, 0.8f, 0.8f);
		material.getSpecular().set(0.6f, 0.6f, 0.6f);
		material.setShininess(8f);
		
		SquareSprite sprite = new SquareSprite(model, texture, normal, shader);
		Translation pos = new Translation();
		sprite.transformations.add(pos);
		pos.getPosition().set(world.getCamera().position);
		pos.getPosition().setZ(pos.getPosition().getZ() - 2);
		return sprite;
	}

	public static Map<String, String> initFunctionsAndShader() throws FileNotFoundException {
		DepthShader.getDepthShader();
		return ShaderFunctions.getFunctions();
	}
	
	public static void createLightsAndFog() {
		Fog.getSysInstance().setMaxDistance(20);
		Fog.getSysInstance().setEnable(false);
		//灯光
		Light light = Lights.getAmbientLight(); 
		light.setEnable(true);
//		light.setSpotCutoff(180);
		//平行光
		light.getPosition().set(128, 128, 128, 0);
		light.getAmbient().set(0.8f, 0.8f, 0.8f);
		light.getDiffuse().set(0.8f, 0.8f, 0.8f);
		light.getSpecular().set(1f, 1f, 1f);
		light.getDirection().set(-1, -1, -1);
		light.getDirection().normalize();
		
		light = Lights.getLight(1); 
		light.setEnable(false);
		light.getPosition().set(12, 12, 12, 1);
		light.getAmbient().set(0.1f, 0.1f, 0.1f);
		light.getDiffuse().set(0.1f, 0.1f, 0.1f);
		light.getSpecular().set(0.2f, 0.2f, 0.2f);
		
		//点光源
//		light.setSpotCutoff(180);
		light.setConstantAttenuation(0.05f);
		light.setLinearAttenuation(0.05f);
		light.setQuadraticAttenuation(0.05f);
		//聚光灯
		light.setSpotCutoff(40);
		light.setSpotExponent(0);
		light.getDirection().set(0f, -1f, 0f);
	}
	
}
